# -*- coding: UTF-8 -*-
import time
import random
import uuid
import os
import json
import copy
from application.app_server_api import AppServer
from application.space_server_api import SpaceServer
from pyLib.databaseManage import APP_DATABASE
from tools.handle_path import data_path
from pyLib.LogManage import LOG
from tools.ReadJson import replace_json_for_value
from application.dimension_api import Dimension
from application.mysql_datatable_api import MysqlDataTable
from application.approval_process_api import ApprovalProcess
from application.smartList_api import SmartList
from application.business_form_api import BusinessForm
from application.business_model_api import BusinessModel
from application.chart_api import Chart
from application.clickhouse_datatable_api import ClickhouseDataTable
from application.dashboard_api import Dashboard
from application.financial_model_api import FinancialModel
from application.list_table_api import ListTable
from application.menu_server_api import Menu
from application.oracle_datatable_api import OracleDataTable
from application.python_api import Python
from application.role_strategy_api import RoleStrategy
from application.sqlserver_datatable_api import SQLServerDataTable
from application.variable_api import VARIABLE


class OperateFolder:
    """文件夹相关操作"""

    def __init__(self, header):
        self.header = header
        self.app_server = AppServer(header)

    def create_folder(self, name, description=None, parentId=0):
        """
        创建文件夹并返回其id
        :param name: 文件夹名称
        :param description:描述
        :param parentId: 父级文件夹id
        """
        payload = {"name": name, "description": description, "parentId": parentId}
        self.app_server.create_folder(payload)
        cond = f"name='{name}' and parent_id='{parentId}'"
        app_id = self.header.get('app')
        res = APP_DATABASE.get_db_data(
            f'app{app_id}_folder_info',
            ['id'],
            condition=cond
        )
        folder_id = res[0][0]
        return folder_id

    def random_creat_folder(self, n1=6, n2=7):
        """随机创建文件夹并返回其所有id"""
        # 第一次循环创建的文件夹数量
        num1 = random.randint(2, n1)
        # 所有文件夹id的列表
        folderIdRes = []
        # 随机创建文件夹
        for one in range(0, num1):
            fileName = uuid.uuid4().hex[::-5]
            folder_id_parent = self.create_folder(f"autocase{fileName}")
            folderIdRes.append(folder_id_parent)
            # 第二次循环创建的文件夹数量
            num2 = random.randint(2, n2)
            self.create_level_folder("autocaseIN", folder_id_parent, num2)
            app_id = self.header.get('app')
            folder_ids = APP_DATABASE.execute_sql(
                f'SELECT id FROM app{app_id}_folder_info WHERE full_path LIKE "%/autocaseIN%"'
            )
            for id in folder_ids:
                folderIdRes.append(id[0])
        # 去重
        folderIdRes = list(set(folderIdRes))
        return folderIdRes

    def create_level_folder(self, folderName, folderId, level):
        """
        创建多层级文件夹
        :param folderName: 文件夹名称
        :param folderId: 需要在哪个文件夹id下创建
        :param level: 创建几个层级
        """
        if level <= 0:
            return
        new_folder_id = self.create_folder(folderName, parentId=folderId)
        self.create_level_folder(folderName, new_folder_id, level - 1)

    def get_all_folderId(self, full_path):
        """
        查询指定文件夹（包括自己）里所有的文件夹id
        :param full_path: 需要查询的文件夹绝对路径
        :return: 文件夹id列表
        """
        res = APP_DATABASE.execute_sql(
            f"SELECT id FROM app{self.header.get('app')}_folder_info WHERE full_path LIKE '{full_path}%'"
        )
        folderId_list = [folderId[0] for folderId in res]
        return folderId_list


class OperateElement:
    """元素相关操作"""

    def __init__(self, header):
        self.header = header
        self.app_server = AppServer(header)
        file_path = os.path.join(data_path, 'action', 'open_element_config.json')
        with open(file_path, 'r', encoding='utf-8') as load_f:
            load_dict = json.load(load_f)
        self.module_server_info = load_dict.get('module-server-info')

    def get_element_list(self, folderIds):
        """
        查询指定目录下所有元素列表信息
        :param folderIds:查询的文件夹id或者id列表
        :return 该文件夹下所有的元素信息 {"id":"","name":"","originParentId":"","type":""}
        """
        if isinstance(folderIds, str):
            data = self.app_server.list_element({"folderId": folderIds}).json().get('data')
            # 生成元素信息,排除文件夹类型
            elementInfo_list = [
                {"id": element_info['id'],
                 "name": element_info['name'],
                 "originParentId": element_info['parentId'],
                 "type": element_info['moduleType']}
                for element_info in data if element_info['moduleType']
            ]
            return elementInfo_list
        elif isinstance(folderIds, list):
            elementInfoRes = []
            for folder_id in folderIds:
                data = self.app_server.list_element({"folderId": folder_id}).json().get('data')
                # 生成元素信息排除文件夹类型
                elementInfo_list = [
                    {"id": element_info['id'],
                     "name": element_info['name'],
                     "originParentId": element_info['parentId'],
                     "type": element_info['moduleType']}
                    for element_info in data if element_info['moduleType']
                ]
                # 合并
                elementInfoRes.extend(elementInfo_list)
            return elementInfoRes
        else:
            LOG.error("查询指定目录下元素列表信息,传入的参数不是list或者str类型")
            return None

    def modify_element_name(self, elementId_list):
        """
        修改元素名称
        """
        if isinstance(elementId_list, int):
            element_list = [elementId_list]
            elementId_list = element_list
        if isinstance(elementId_list, list):
            # 修改信息
            modify_info = []
            for elementId in elementId_list:
                # 修改之前的名字
                oldelement_name, oldelement_type = APP_DATABASE.execute_sql(
                    f"""
                    SELECT
                        element_name,
                        element_type
                    FROM
	                    app{self.header.get('app')}_element_info
                    WHERE
	                    id = '{elementId}'
                """)[0]
                newElementName = "newName" + uuid.uuid4().hex[::-5]
                self.app_server.modify_element_name({"id": elementId, "newElementName": newElementName})
                modify_info.append(f"【{oldelement_name}】元素【{oldelement_type}】类型名称被修改为【{newElementName}】")
            return modify_info

    def move_elements_to_randomFolder(self, elementInfoList, targetfolderIdList):
        """
        批量移动元素到随机文件夹里
        :param elementInfoList: 需要移动的元素信息列表[{"id": "1","name": "","originParentId": "0","type": ""}]
        :param targetfolderIdList: 目标文件夹id列表
        """
        for element_info in elementInfoList:
            # 随机选一个目标文件夹id
            targetParentId = random.choice(targetfolderIdList)
            element_info['targetParentId'] = targetParentId
        # 批量移动元素
        self.app_server.move_folders_elements(elementInfoList)
        moveElementInfo = [
            f"【{elementInfo.get('name')}】元素【{elementInfo.get('type')}】类型从【{elementInfo.get('originParentId')}】移动到了【{elementInfo.get('targetParentId')}】"
            for elementInfo in elementInfoList]
        return moveElementInfo

    def query_operation_log(self, check_result, move_log, rename_log):
        """
        根据检查的结果查询相关元素的移动操作信息和重命名操作信息，从而获取到元素本身和关联元素的操作过程
        :param check_result: 检查元素是否能打开的结果，[{"元素名称": '',"元素类型":'',"接口地址": '', "请求内容": '', "响应code":'',"响应结果": r.json()},......]
        :param move_log: ['【】元素【】类型从【】移动到了【】',.....]
        :param rename_log: ['【】元素【】类型名称被修改为【】', .....]
        :return 增加元素本身操作过程和关联元素操作过程到检查结果里
        """
        for result in check_result:
            # 元素本身操作过程
            ele_op = []
            # 关联元素操作过程
            relationEle_op = []
            eleName = result.get("元素名称")
            eleType = result.get("元素类型")
            # 获取元素本身被移动的信息
            for moveInfo in move_log:
                if f"【{eleName}】元素【{eleType}】类型" in moveInfo:
                    ele_op.append(moveInfo)
            # 获取元素本身重命名信息
            for renameInfo in rename_log:
                if f"修改为【{eleName}】" in renameInfo:
                    ele_op.append(renameInfo)
                    # 获取重命名前的名字和类型
                    oldName = renameInfo.split('名称被修改为')[0]
                    # 检查下重命名前是否被移动过
                    for moveInfo in move_log:
                        if oldName in moveInfo:
                            ele_op.append(moveInfo)
            element_relation = APP_DATABASE.execute_sql(
                f"SELECT relation_element_name,relation_element_type FROM app{self.header.get('app')}_element_relation WHERE element_name='{eleName}' AND element_type='{eleType}'")
            # 获取关联元素被移动的信息
            for moveInfo in move_log:
                for relation_element_name, relation_element_type in element_relation:
                    if f"【{relation_element_name}】元素【{relation_element_type}】类型" in moveInfo:
                        relationEle_op.append(moveInfo)
            # 获取关联元素重命名信息
            for renameInfo in rename_log:
                for relation_element_name, relation_element_type in element_relation:
                    if f"修改为【{relation_element_name}】" in renameInfo:
                        relationEle_op.append(renameInfo)
                        # 获取重命名前的名字和类型
                        oldName = renameInfo.split('名称被修改为')[0]
                        # 检查下重命名前是否被移动过
                        for moveInfo in move_log:
                            if oldName in moveInfo:
                                relationEle_op.append(moveInfo)
            # 去重
            ele_op = list(set(ele_op))
            relationEle_op = list(set(relationEle_op))
            if not ele_op:
                ele_op = ''
            if not relationEle_op:
                relationEle_op = ''
            result['元素本身操作过程'] = ele_op
            result['关联元素操作过程'] = relationEle_op
        return check_result

    def request_open_element_api(self, element_info: dict):
        """
        检测元素打开接口
        :param element_info: 元素信息
        :return: [{"元素名称": elementName, "元素类型": moduleType, "接口地址": url, "请求内容": elementInfo,"响应code": response_code, "响应结果": response}]
        """
        # 元素版本
        module_version = element_info.get('moduleVersion')
        # 元素类型
        module_type = element_info.get('elementType')
        # 获取元素名称
        elementName = element_info.get('elementName')
        # 获取文件夹id
        folderId = element_info.get('folderId')
        # 获取对应组件元素请求信息
        module_request_info = self.module_server_info.get(module_type)
        result = []
        if module_request_info:
            # 获取调用类名
            className = module_request_info.get("className")
            if module_request_info.get(module_version):
                # 实例化调用的类
                callClassName = eval(className)(version=module_version, headers=self.header)
                for pay_info in module_request_info.get(module_version):
                    # 获取调用方法名
                    methodName = pay_info.get("methodName")
                    # 获取请求内容
                    payload = copy.deepcopy(pay_info.get("payload"))
                    # 替换请求内容
                    for search_value, replace_value in {"#elementName#": elementName, "#folderId#": folderId}.items():
                        if isinstance(payload, dict):
                            replace_json_for_value(payload, search_value, replace_value)
                        elif isinstance(payload, list):
                            replace_json_for_value(payload[0], search_value, replace_value)
                    # 调用请求方法
                    r = getattr(callClassName, methodName)(payload)
                    try:
                        response = r.json()
                        response_code = response.get('code')
                    except:
                        response = None
                        response_code = None
                    result.append(
                        {"元素名称": elementName,
                         "元素类型": module_type,
                         "接口地址": callClassName.api_url,
                         "请求内容": payload,
                         "响应code": response_code,
                         "响应结果": response}
                    )
            else:
                LOG.warning(f"元素【{elementName}】类型【{module_type}】未配置【{module_version}】版本")
        else:
            LOG.warning(f"检查元素打开模板未配置组件类型：{module_type}")
        return result

    def list_all_elementsInfo(self):
        """查询所有元素信息(排除"PAGE","DIR","BF")"""
        data = self.app_server.list_all_elements().json().get('data')
        elementsInfo = [{"elementName": elementInfo.get('name'),
                         "name": elementInfo.get('name'),
                         "elementType": elementInfo.get('elementType'),
                         "folderId": elementInfo.get('parentId'),
                         "moduleId": elementInfo.get('moduleId'),
                         "moduleVersion": elementInfo.get('moduleVersion')}
                        for elementInfo in data if elementInfo.get('elementType') not in ("PAGE", "DIR", "BF")
                        ]
        return elementsInfo

    def check_elements_open(self, elementList):
        """
        检查元素是否能正常打开
        根据元素类型调用编辑视图和使用视图的查询接口，判断元素是否能正常打开
        :param elementList: 需要检查的元素列表{ 'elementName': '',
                                              'elementType': '',
                                              'folderId': '',
                                              'moduleId': '',
                                              'name': '',
                                              'serverName': ''}
        :return: 检查结果，有报错的接口才有内容
        """
        # 检查结果
        check_result = []
        for elementInfo in elementList:
            # 调用元素打开接口获取结果
            result = self.request_open_element_api(elementInfo)
            for one in result:
                response = one.get('响应结果')
                if response:
                    # 响应状态
                    status = response.get('status')
                    # 响应信息
                    message = response.get('message')
                    # 响应的data
                    data = response.get('data')
                    if status != True or message:
                        check_result.append(one)
                    elif isinstance(data, dict):
                        message = data.get('messages')
                        if message:
                            check_result.append(one)
                else:
                    check_result.append(one)
        return check_result

    @staticmethod
    def random_select_elements(elementList):
        """
        随机选取列表中的元素并返回
        :param elementList:元素列表
        :return:选中的元素列表
        """
        lenth = len(elementList)
        # 随机生成选中的数量
        num = random.randint(lenth // 2, lenth)
        elements = random.sample(elementList, num)
        return elements

    def check_import_status(self, log_id):
        """
        检查导入是否成功
        :param log_id:
        :return:导入失败结果
        """
        while True:
            try:
                data = SpaceServer.element_import_log(log_id, self.header).json().get('data')
                if data.get('status') == "SUCCESS":
                    break
                # 如果是部分成功
                elif data.get('status') == "PART_SUCCESS" or data.get('status') == "FAIL":
                    logDetailList = data.get('logDetailList')
                    # 找到失败的结果
                    fail = [logDetail for logDetail in logDetailList if logDetail.get('status') != "SUCCESS"]
                    return fail
                time.sleep(2)
            except Exception as e:
                print(e)


if __name__ == '__main__':
    from application.general_create import COOKIE, APP_ID, SPACE_ID

    headers = {"cookie": COOKIE, "language": "zh-cn", "app": APP_ID, "space": SPACE_ID}
    import pprint

    OPE = OperateElement(headers)
    r = OPE.list_all_elementsInfo()
    check_result = OPE.check_elements_open(r)
    pprint.pprint(check_result)
    # # excel标题行
    # check_result.insert(0, ('元素名称', '元素类型', '响应code', '响应结果', '接口地址', '请求内容'))
    # # 写入到excel里
    # from pyLib.excelManage import ParseExcel
    #
    # ParseExcel().write_rows_data(check_result, save_file_path="./res.xlsx")
